﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace AhpilyServer
{
    /// <summary>
    /// 封装的客户端对象
    /// </summary>
   public class ClientPeer
    {
        public string id;
        public Socket ClientSocket { get; set; }
        public ClientPeer()
        {
            this.ReceiveArgs = new SocketAsyncEventArgs();
            this.ReceiveArgs.UserToken = this;
            this.ReceiveArgs.SetBuffer(new byte[1024], 0, 1024);
            this.SendArgs = new SocketAsyncEventArgs();
            this.SendArgs.Completed += SendArgs_Completed;
        }


        #region 接收数据

        //回调函数
        public delegate void ReceiveCompleted(ClientPeer client, ByteBuffer msg);
        /// <summary>
        /// 消息解析完成的回调
        /// </summary>
        public ReceiveCompleted receiveCompleted;
        /// <summary>
        /// 接收到数据存到缓存区
        /// </summary>
        private List<byte> dataCache = new List<byte>();//接收数据数组
        /// <summary>
        /// 接收的异步套接字请求
        /// </summary>
        public SocketAsyncEventArgs ReceiveArgs { get; set; }
        /// <summary>
        /// 是否正在处理接受的数据 默认为否 
        /// </summary>
        private bool isReceivePrcoess = false;
        /// <summary>
        /// 设置连接的对象 
        /// </summary>
        /// <param name="socket"></param>
        public void SetSocket(Socket socket)
        {
            ClientSocket = socket;
        }

        /// <summary>
        /// 自身处理数据包
        /// </summary>
        /// <param name="packet"></param>
        public void StartReceive(byte[] packet)
        {
            dataCache.AddRange(packet);//添加接受的数据
            if (!isReceivePrcoess)
            {
                processReceive();
            }
        }

        private void processReceive()
        {
            isReceivePrcoess = true;
            byte[] data = EncodeTool.DecodePacket(ref dataCache); //???
            if (data == null)
            {
                isReceivePrcoess = false;
                return;
            }

            //需要再次转成一个具体的类型供我们使用
            //SocketMsg msg = EncodeTool.DecodeMsg(data);
            ByteBuffer msg = EncodeTool.DecodeMsg(data);
            //回调上层 ServerPeer
            if (receiveCompleted != null)
            {
                receiveCompleted(this, msg);
            }
            processReceive();  //尾递归
        }
        ////粘包拆包问题 :解决策略.消息头和消息尾
        ////例子：发送消息：12345
        //void test()
        //{
        //    byte[] bt = Encoding.Default.GetBytes("12345");
        //    //消息头指消息的长度 消息尾指具体的消息
        //    //构造
        //    int length = bt.Length;
        //    byte[] bt1 = BitConverter.GetBytes(length);
        //    //得到消息是bt1+bt
        //    //读取： int length=前四个字节转换为int类型 然后读取这个长度的数据
        //}
        #endregion

        #region 断开连接

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            dataCache.Clear();//清空数据
            isReceivePrcoess = false;
            sendQueue.Clear();
            isSendProcess = false;


            ClientSocket.Shutdown(SocketShutdown.Both); //禁止发送接收
            ClientSocket.Close(); //断开连接释放资源
            ClientSocket = null;
        }
        #endregion

        #region 发送数据

        /// <summary>
        /// 发送的一个消息的队列
        /// </summary>
        private Queue<byte[]> sendQueue = new Queue<byte[]>();

        private bool isSendProcess = false; //是否发送 默认为否 
        /// <summary>
        /// 发送的异步套接字操作
        /// </summary>
        private SocketAsyncEventArgs SendArgs;
        /// <summary>
        /// 发送的时候 发现断开连接的回调
        /// </summary>
        /// <param name="client"></param>
        /// <param name="reason"></param>
        public delegate void SendDisconnect(ClientPeer client, string reason);

        public SendDisconnect sendDisconnect;
        /// <summary>
        /// 发送网络消息
        /// </summary>
        /// <param name="opCode">操作码</param>
        /// <param name="subCode">子操作</param>
        /// <param name="value"></param>
        public void Send(int code, byte[] bytes)
        {
            //SocketMsg msg = new SocketMsg(opCode, subCode, value);
            byte[] data = EncodeTool.EncodeMsg(code, bytes);
            byte[] packet = EncodeTool.EncodePacket(data);
            sendQueue.Enqueue(packet);//信息存入消息队列
            if (!isSendProcess)
            {
                send();
            }
        }
        /// <summary>
        /// 处理发送的数据
        /// </summary>
        private void send()
        {
            isSendProcess = true;
            if (sendQueue.Count == 0)//消息队列无消息
            {
                isSendProcess = false;
                return;
            }
            byte[] packet = sendQueue.Dequeue(); //取出一条数据 
            SendArgs.SetBuffer(packet, 0, packet.Length); //设置消息 发送的异步套接字操作 的发送数据缓冲区 参数 消息 偏移量 长度
            bool result = ClientSocket.SendAsync(SendArgs);
            if (result == false)
            {
                sendCompleted();
            }
        }

        private void SendArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            sendCompleted();
        }
        /// <summary>
        /// 异步发送完成的时候调用
        /// </summary>
        private void sendCompleted()
        {
            if (SendArgs.SocketError != SocketError.Success)
            {
                //发送出错 客户端断开连接
                sendDisconnect(this, SendArgs.SocketError.ToString());
            }
            else
            {
                send();
            }
        }

        #endregion
    }
}
